package com.wenbing.miio.core;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 固定大小的缓冲队列
 * 超出最大容量，丢弃最早的数据
 * 无数据阻塞线程
 * @author wen bing
 * @projectName: miio
 * @package: com.wenbing.miio.util
 * @className: List
 * @date 2024/09/21 17:55
 **/
public class BufferQueue<T> {

    private class Node<T> {
        T data;
        Node<T> next;

        private Node(T data) {
            this.data = data;
        }
    }

    private Lock lock;

    private Condition notEmptyCondition;

    private Node<T> head;

    private Node<T> tail;

    private int capacity;

    private int size;

    public BufferQueue() {
        this(16);
    }

    public BufferQueue(int capacity) {
        assert capacity > 0;
        this.capacity = capacity;
        this.head = new Node<>(null);
        this.head.next = this.head;
        this.tail = this.head;
        this.lock = new ReentrantLock();
        this.notEmptyCondition = this.lock.newCondition();
    }

    /**
     * 入队
     * 超出最大容量，丢弃最早的数据
     * @param data
     */
    public void unshift(T data) {
        lock.lock();
        try {
            if (size == capacity) {
                update(data);
                return;
            }
            insertTail(data);
        }
        finally {
            lock.unlock();
        }
    }
    private void update(T data) {
        head = head.next;
        tail = tail.next;
        tail.data = data;
        notEmptyCondition.signal();
    }

    private void insertTail(T data) {
        Node<T> node = new Node<>(data);
        node.next = tail.next;
        tail.next = node;
        tail = node;
        size++;
        notEmptyCondition.signal();
    }

    /**
     * 出队
     * 无数据返回null
     * @return
     */
    public T pop() {
        lock.lock();
        try {
            if (isEmpty()) {
                return null;
            }
            head = head.next;
            return head.data;
        }
        finally {
            lock.unlock();
        }

    }

    /**
     * 出队
     * 无数据阻塞线程
     * @return
     * @throws InterruptedException
     */
    public T shift() throws InterruptedException {
        lock.lockInterruptibly();
        try {
            while (isEmpty())
                notEmptyCondition.await();
            head = head.next;
            size--;
            return head.data;
        }
        finally {
            lock.unlock();
        }
    }

    public boolean isEmpty() {
        return this.size == 0;
    }
}
